/*
 *   Copyright (c) 2018. 刘路 All rights reserved
 *   版权所有 刘路 并保留所有权利 2018.
 *   ===============================================================
 *   这不是一个自由软件！您只能在不用于商业目的的前提下对程序代码进行修改和
 *   使用。不允许对程序代码以任何形式任何目的的再发布。如果项目发布携带作者
 *   认可的特殊 LICENSE 则按照 LICENSE 执行，废除上面内容。请保留原作者信息。
 *   ================================================================
 *   刘路（feedback@zhoyq.com）于 2018. 创建
 *   http://zhoyq.com
 */

package com.zhoyq.helper;

import org.apache.sanselan.ImageFormat;
import org.apache.sanselan.formats.jpeg.JpegConstants;
import org.apache.sanselan.formats.png.PngConstants;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.*;
import java.util.Arrays;

/**
 * 图片处理工具类
 *
 * @author 刘路
 */
public class ImgHelper {

    public static String base64String(byte[] image,String format) throws UnsupportedEncodingException {
        return "data:image/" + format + ";base64," + new String(Code.base64Encode(image),"ASCII");
    }

    public static int[][] getRData(File image) {
        try {
            BufferedImage bimg = ImageIO.read(image);
            int width = bimg.getWidth();
            int height = bimg.getHeight();
            int[][] data = new int[width][height];
            int[][] R = new int[width][height];
            for(int i=0; i<width; i++){
                for(int j=0; j<height; j++) {
                    //得到图片的位图矩阵
                    data[i][j] = bimg.getRGB(i,j);
                    //得到R通道数据
                    R[i][j] = (data[i][j]&0xff0000)>>16;
                }
            }
            return R;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static int[][] getGData(File image) {
        try {
            BufferedImage bimg = ImageIO.read(image);
            int width = bimg.getWidth();
            int height = bimg.getHeight();
            int[][] data = new int[width][height];
            int[][] G = new int[width][height];
            for(int i=0; i<width; i++){
                for(int j=0; j<height; j++) {
                    //得到图片的位图矩阵
                    data[i][j] = bimg.getRGB(i,j);
                    //得到G通道数据
                    G[i][j] = (data[i][j]&0xff00)>>8;
                }
            }
            return G;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static int[][] getBData(File image) {
        try {
            BufferedImage bimg = ImageIO.read(image);
            int width = bimg.getWidth();
            int height = bimg.getHeight();
            int[][] data = new int[width][height];
            int[][] B = new int[width][height];
            for(int i=0; i<width; i++){
                for(int j=0; j<height; j++) {
                    //得到图片的位图矩阵
                    data[i][j] = bimg.getRGB(i,j);
                    //得到B通道数据
                    B[i][j] = data[i][j]&0xff;
                }
            }
            return B;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static int[][] LBSofR(File image, byte[] secretData) {
        //原图片的R通道
        int[][] R = getRData(image);
        //记录读取到的比特位数
        int count = 0;
        for(int i=0; i<secretData.length; i++){
            int a = secretData[i];
            for(int j=0; j<8; j++) {
                //依次获取字节的每一位
                int LB = a&0x1;
                int w = count/R.length;
                int h = count%R.length;
                //最低位设置为秘密信息比特流
                R[w][h] = (R[w][h]&0xfe) + LB;
                System.out.println(w + " " + h + " : " + R[w][h] + " - " + LB);
                count++;
                a = a>>1;
            }
        }
        return R;
    }

    public static int[][] LBSofG(File image, byte[] secretData) {
        //原图片的G通道
        int[][] G = getGData(image);
        //记录读取到的比特位数
        int count = 0;
        for(int i=0; i<secretData.length; i++){
            int a = secretData[i]&0xff;
            for(int j=0; j<8; j++) {
                //依次获取字节的每一位
                int LB = a&0x1;
                int w = count/G.length;
                int h = count%G.length;
                //最低位设置为秘密信息比特流
                G[w][h] = (G[w][h]&0xfe) + LB;
                count++;
                a = a>>1;
            }
        }
        return G;
    }

    public static int[][] LBSofB(File image, byte[] secretData) {
        //原图片的B通道
        int[][] B = getBData(image);
        //记录读取到的比特位数
        int count = 0;
        for(int i=0; i<secretData.length; i++){
            int a = secretData[i]&0xff;
            for(int j=0; j<8; j++) {
                //依次获取字节的每一位
                int LB = a&0x1;
                int w = count/B.length;
                int h = count%B.length;
                //最低位设置为秘密信息比特流
                B[w][h] = (B[w][h]&0xfe) + LB;
                count++;
                a = a>>1;
            }
        }
        return B;
    }

    public static void writeEncodeImage(int[][] newChannel, char channelCode, File image,File out) {
        try {
            BufferedImage bimg = ImageIO.read(image);
            int width = bimg.getWidth();
            int height = bimg.getHeight();
            switch(channelCode) {
                case 'R':
                    for(int i=0; i<width; i++){
                        for(int j=0; j<height; j++) {
                            int rgb = bimg.getRGB(i,j);
                            int r = rgb&0xff0000;
                            int g = rgb&0xff00;
                            int b = rgb&0xff;
                            //对R通道更新
                            int buf = newChannel[i][j]<<16;
                            if(r != buf){
                                rgb = buf + g + b + 0xff000000;
                                System.out.print(i + " " + j + " : " + Integer.toHexString(r) + " " + Integer.toHexString(newChannel[i][j]) +" "
                                        + Integer.toHexString(rgb)+" ");
                                bimg.setRGB(i, j, rgb);
                                System.out.println(Integer.toHexString(bimg.getRGB(i, j)));

                            }
                        }
                    }
                    break;
                case 'G':
                    for(int i=0; i<width; i++){
                        for(int j=0; j<height; j++) {
                            int rgb = bimg.getRGB(i,j);
                            int r = rgb&0xff0000;
                            int b = rgb&0xff;
                            //对G通道更新
                            rgb = r + newChannel[i][j]*256 + b + 0xff000000;
                            bimg.setRGB(i, j, rgb);
                        }
                    }
                    break;
                case 'B':
                    for(int i=0; i<width; i++){
                        for(int j=0; j<height; j++) {
                            int rgb = bimg.getRGB(i,j);
                            //对B通道更新
                            rgb = (rgb&0xffffff00) + newChannel[i][j];
                            bimg.setRGB(i, j, rgb);
                        }
                    }
                    break;
                default:
                    break;
            }
            System.out.println("-"+Integer.toHexString(bimg.getRGB(0,0)));
            ImageIO.write( bimg ,"png", out);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static byte[] getSecret(int size, int channelCode, File encodeImage) {
        try {
            BufferedImage bimg = ImageIO.read(encodeImage);
            int width = bimg.getWidth();
            int height = bimg.getHeight();
            byte[] secretData = new byte[size];
            int count = 1;
            int index = 0;
            int temp = 0;
            for(int i=0; i<width; i++){
                for(int j=0; j<height; j++) {
                    int channel = 0;
                    int rgb = bimg.getRGB(i,j);
                    switch(channelCode) {
                        case 'R':
                            //从R通道读取
                            channel = (rgb&0xff0000)>>16;
                            break;
                        case 'G':
                            //从G通道读取
                            channel = (rgb&0xff00)>>8;
                            break;
                        case 'B':
                            //从B通道读取
                            channel = rgb&0xff;
                            break;
                        default:
                            break;
                    }
                    //从低比特位往高比特位补充
                    temp += (channel&0x1)*Math.pow(2, (count-1)%8);
                    System.out.println(i + " " + j + " : "+ (channel&0x1) + " " + temp + " "
                            +Integer.toHexString(rgb));
                    //读取到一字节数据
                    if((count++)%8 == 0) {
                        secretData[index++] = (byte)temp;
                        temp = 0;
                    }
                    if(index == size) {
                        return secretData;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static int encode(File image,File out,String content){
        byte[] data = new byte[0];
        boolean flag = false;
        try {
            data = content.getBytes("ASCII");
            System.out.println(Arrays.toString(data));
            flag = true;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        if(flag){
            int[][] R = LBSofR(image,data);
            writeEncodeImage(R,'R',image,out);
            return data.length;
        }else{
            return 0;
        }
    }

    public static String decode(int size,File image){
        try {
            return new String(getSecret(size,'R',image),"ASCII");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static boolean verify(int size,File image,String content) {
        String decodeStr = decode(size,image);
        return content.equals(decodeStr);
    }

    /**
     * 采用指定宽度、高度或压缩比例 的方式对图片进行压缩
     * @param imgsrc 源图片地址
     * @param imgdist 目标图片地址
     * @param widthdist 压缩后图片宽度（当rate==null时，必传）
     * @param heightdist 压缩后图片高度（当rate==null时，必传）
     * @param rate 压缩比例
     */
    public static void resize(String imgsrc, String imgdist, int widthdist,
                              int heightdist, Float rate,String formatName) {
        try {
            File srcfile = new File(imgsrc);
            // 检查文件是否存在
            if (!srcfile.exists()) {
                return;
            }
            // 如果rate不为空说明是按比例压缩
            if (rate != null && rate > 0) {
                // 获取文件高度和宽度
                int[] results = getImgSize(srcfile);
                if (results == null || results[0] == 0 || results[1] == 0) {
                    return;
                } else {
                    widthdist = (int) (results[0] * rate);
                    heightdist = (int) (results[1] * rate);
                }
            }
            // 开始读取文件并进行压缩
            Image src = javax.imageio.ImageIO.read(srcfile);
            BufferedImage tag = new BufferedImage((int) widthdist,
                    (int) heightdist, BufferedImage.TYPE_INT_RGB);

            tag.getGraphics().drawImage(
                    src.getScaledInstance(widthdist, heightdist,
                            Image.SCALE_SMOOTH), 0, 0, null);

            RenderedImage im = (RenderedImage)tag;
            ImageIO.write( im ,formatName, new File(imgdist));
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 获取图片宽度和长度
     *
     * @param file 图片文件
     * @return 宽度
     */
    public static int[] getImgSize(File file) {
        InputStream is = null;
        BufferedImage src = null;
        int result[] = { 0, 0 };
        try {
            is = new FileInputStream(file);
            src = javax.imageio.ImageIO.read(is);
            // 得到源图宽
            result[0] = src.getWidth(null);
            // 得到源图高
            result[1] = src.getHeight(null);
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }


    public static void markBat(String waterImgPath, String srcImgDir,
                            String targerDir, double degree,float alpha,int interval) {
        markBat(new File(waterImgPath),new File(srcImgDir),new File(targerDir),degree,alpha,interval);
    }

    private static void markBat(File waterImg, File src,
                               File target, double degree,float alpha,int interval) {
        if(src.isDirectory()){
            for(File file:src.listFiles()){
                if(file!=null){
                    File newTarget = new File(target.getAbsolutePath()+File.separator+file.getName());
                    if(!newTarget.getParentFile().exists()){
                        newTarget.getParentFile().mkdirs();
                    }
                    markBat(waterImg,file,newTarget,degree,alpha,interval);
                }
            }
        }else{
            mark(waterImg.getAbsolutePath(),src.getAbsolutePath(),target.getAbsolutePath(),
                    degree,alpha,interval);
        }
    }

    /**
     * 给图片添加水印图片、可设置水印图片旋转角度
     *
     * @param waterImgPath 水印图片路径
     * @param srcImgPath 源图片路径
     * @param targerPath 目标图片路径
     * @param degree 水印图片旋转角度
     * @param alpha 透明度
     * @param interval 水印图片间隔
     */
    public static void mark(String waterImgPath, String srcImgPath,
                            String targerPath, double degree,float alpha,int interval) {
        OutputStream os = null;
        try {

            Image srcImg = ImageIO.read(new File(srcImgPath));

            BufferedImage buffImg = new BufferedImage(srcImg.getWidth(null),
                    srcImg.getHeight(null), BufferedImage.TYPE_INT_RGB);

            // 1、得到画笔对象
            Graphics2D g = buffImg.createGraphics();

            // 2、设置对线段的锯齿状边缘处理
            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g.drawImage(srcImg.getScaledInstance(srcImg.getWidth(null), srcImg
                    .getHeight(null), Image.SCALE_SMOOTH), 0, 0, null);
            Font font = new Font("微软雅黑",Font.ITALIC, 20);
            // 设置画笔字体
            g.setFont(font);
            g.setColor(Color.RED);
            g.drawString("©zhoyq.com 衷于栖 2018",
                    srcImg.getWidth(null)-280,srcImg.getHeight(null)-30);
            // 3、设置水印旋转
            if (0 != degree) {
                g.rotate(Math.toRadians(degree),
                        (double) buffImg.getWidth() / 2, (double) buffImg
                                .getHeight() / 2);
            }

            // 4、水印图片的路径 水印图片一般为gif或者png的，这样可设置透明度
            ImageIcon imgIcon = new ImageIcon(waterImgPath);

            // 5、得到Image对象。
            Image img = imgIcon.getImage();

            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,
                    alpha));

            // 6、水印图片的位置
            for (int height = interval + imgIcon.getIconHeight() - buffImg.getHeight()/2; height < buffImg
                    .getHeight() * 2; height = height +interval+ imgIcon.getIconHeight()) {
                for (int weight = interval + imgIcon.getIconWidth() - buffImg.getWidth()/2; weight < buffImg
                        .getWidth() * 2; weight = weight +interval+ imgIcon.getIconWidth()) {
                    g.drawImage(img, weight - imgIcon.getIconWidth(), height
                            - imgIcon.getIconHeight(), null);
                }
            }
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
            // 7、释放资源
            g.dispose();

            // 8、生成图片
            os = new FileOutputStream(targerPath);
            ImageIO.write(buffImg, "JPG", os);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != os) {
                    os.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     *
     * @param str 输入的文字
     * @param font 字体
     * @param outFile 输出的文件
     * @param width 宽度
     * @param height 高度
     */
    public static void createImageByStr(String str, Font font, File outFile,
                                   Integer width, Integer height) throws Exception {
        // 创建图片
        BufferedImage image = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_BGR);
        Graphics g = image.getGraphics();
        g.setClip(0, 0, width, height);
        g.setColor(Color.white);
        // 先用黑色填充整张图片,也就是背景
        g.fillRect(0, 0, width, height);
        // 在换成黑色
        g.setColor(Color.black);
        // 设置画笔字体
        g.setFont(font);
        // 用于获得垂直居中y
        Rectangle clip = g.getClipBounds();
        FontMetrics fm = g.getFontMetrics(font);
        int ascent = fm.getAscent();
        int descent = fm.getDescent();
        int y = (clip.height - (ascent + descent)) / 2 + ascent;
        // 256 340 0 680
        for (int i = 0; i < 6; i++) {
            // 画出字符串
            g.drawString(str, i * 680, y);
        }
        g.dispose();
        // 输出png图片
        ImageIO.write(image, "png", outFile);
    }
}
